#include "page_table.H"
#include "paging_low.H"

FramePool     * PageTable::kernel_mem_pool;    /* Frame pool for the kernel memory */
FramePool     * PageTable::process_mem_pool;   /* Frame pool for the process memory */
unsigned long   PageTable::shared_size;        /* size of shared address space */
PageTable     * PageTable::current_page_table;


PageTable::PageTable() {
	page_directory	= (unsigned long *) (kernel_mem_pool->get_frame() * PAGE_SIZE);
	unsigned long *page_table 	= (unsigned long *) (kernel_mem_pool->get_frame() * PAGE_SIZE);
	unsigned long address = 0; // holds the physical address of where a page is
	unsigned int i;

	// map the first 4MB of memory as present
	for(i=0; i<ENTRIES_PER_PAGE; i++)
	{
		page_table[i] = address | 3; // attribute set to: supervisor level, read/write, present(011 in binary)
		address += PAGE_SIZE;
	}

	// fill the first entry of the page directory
	page_directory[0] = (unsigned long) (page_table); // attribute set to: supervisor level, read/write, present(011 in binary)
	page_directory[0] |= 3;

	// fill the rest of the page directory
	for(i=1; i<ENTRIES_PER_PAGE; i++)
	{
		page_directory[i] = 0 | 2; // attribute set to: supervisor level, read/write, not present(010 in binary)
	}
}

void PageTable::load() {
	current_page_table = this;
}

void PageTable::enable_paging() {
	write_cr3((unsigned long)(current_page_table->page_directory)); // put that page directory address into CR3
	write_cr0(read_cr0() | 0x80000000); // set the paging bit in CR0 to 1
}

void PageTable::handle_fault(REGS * _r) {
	int err_code = _r->err_code & 7;
	unsigned long address = read_cr2();
	
	if ((err_code & 1) == 0) {
		// PAGE NOT PRESENT
		unsigned long * page_table;
		unsigned long * page_dir = current_page_table->page_directory;
		unsigned long pageDirIndex = (address & DIR_MASK) >> 22;
		unsigned long pageTableIndex = (address & TABLE_MASK) >> 12;
		unsigned long * pageEntry;
		//Console::putui(pageDirIndex);
		//Console::putui(pageTableIndex);
		//Console::putui(address);


		page_table = (unsigned long *) page_dir[pageDirIndex];

		if ((page_dir[pageDirIndex] & 1) == 0) {
			page_table = (unsigned long *) (kernel_mem_pool->get_frame() * PAGE_SIZE);

			for(int i=0; i<ENTRIES_PER_PAGE; i++) {
				unsigned long frameNo = process_mem_pool->get_frame();
				if (frameNo == 0) Console::puts("PANIC\n");
				pageEntry = (unsigned long *) (frameNo * PAGE_SIZE);							
				page_table[i] = (unsigned long) (pageEntry) | 3; // attribute set to: user level, read/write, present(111 in binary)
				Console::putui(i);				
				Console::putui(frameNo);			
			}
							
			page_dir[pageDirIndex] = (unsigned long) (page_table); // attribute set to: supervisor level, read/write, present(011 in binary)
			page_dir[pageDirIndex] |= 3;

		}

		pageEntry = (unsigned long *) page_table[pageTableIndex];
		if ((page_table[pageTableIndex] & 1) == 0) {
			pageEntry = (unsigned long *) (process_mem_pool->get_frame() * PAGE_SIZE);							
			page_table[pageTableIndex] = (unsigned long) (pageEntry); // attribute set to: user level, read/write, present(111 in binary)
			page_table[pageTableIndex] |= 7;

		}
		else {
			//Console::puts("PAGE ENTRY ALREADY PRESENT - CANNOT OCCUR!\n");
			//for(;;);
		}

	}	
	else {
		Console::puts("PROTECTION FAULT - CANNOT OCCUR!\n");
		for(;;);
		//PROTECTION FAULT
	}
}

void PageTable::init_paging(FramePool * _kernel_mem_pool,
                          FramePool * _process_mem_pool,
                          const unsigned long _shared_size) {
     kernel_mem_pool  = _kernel_mem_pool;
     process_mem_pool = _process_mem_pool;
     shared_size      = _shared_size;
}
